
#include "http_support.h"
#include <QStandardPaths>
#include <QNetworkReply>
#include <QImage>
#include <iostream>

CHttpSupport::CHttpSupport(QObject *parent)
        :QObject(parent)
        , net_access_manager_(new QNetworkAccessManager(this))
{

}

CHttpSupport::~CHttpSupport() = default;

void CHttpSupport::clearRp(QNetworkReply *rp)
{
    if (rp)
    {
        QString url = rp->request().url().toString();
        QString postMD5 = rp->property("postMD5").toString();

        if (postMD5.isEmpty())
        {
            //清理对应缓存
            download_data_cache_.remove(url);
            //解除正在处理状态
            processing_rq_.remove(url);
        }
        else
        {
            //清理对应缓存
            download_data_cache_.remove(postMD5);
            //解除正在处理状态
            processing_rq_.remove(postMD5);
        }

        redirect_map_.remove(url);
        redirect_map_.remove(postMD5);

        qDebug() << "delete cache, url:" << url << " postMOD5:" << postMD5;
        rp->deleteLater();
    }
}

void CHttpSupport::onHttpGetRspProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if (sender() == nullptr)
    {
        return;
    }
    auto rp = qobject_cast<QNetworkReply*>(sender());
    if (rp == nullptr)
    {
        return;
    }
    if (bytesTotal <= 0)
    {
        return;
    }
    QString url = rp->url().toString();
    download_data_cache_[url].append(rp->readAll());
}

void CHttpSupport::onHttpGetRspFinished()
{
    if (sender() == nullptr)
    {
        return;
    }
    auto rp = qobject_cast<QNetworkReply*>(sender());
    if (rp == nullptr)
    {
        return;
    }
    QByteArray rpData;
    QString url = rp->url().toString();

    int statusCode = rp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    QString strUrl = rp->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();

    switch (statusCode)
    {
        case 200: // ok
        {
            rpData = download_data_cache_[url];
            QString redirectUrl = redirect_map_[url];
            if (redirectUrl.isEmpty())
            {
                emit httpGetRspReady(url, rpData);
            }
            else
            {
                emit httpGetRspReady(redirectUrl, rpData);
            }

        }
            break;
        case 301:
        case 302: // redirect
        {
            if (!strUrl.isEmpty())
            {
                QString tUrl = redirect_map_[url];
                if (tUrl.isEmpty())
                    redirect_map_[strUrl] = url;
                else
                    redirect_map_[strUrl] = tUrl;

                httpGet(strUrl);
            }
        }
            break;
        default: // error
        {
            QString redirectUrl = redirect_map_[url];
            if (redirectUrl.isEmpty())
            {
                emit httpGetRspReady(url, QByteArray());
            }
            else
            {
                emit httpGetRspReady(redirectUrl, QByteArray());
            }
        }
            break;
    }
    clearRp(rp);
}

void CHttpSupport::onHttpPostRspProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if (sender() == nullptr)
    {
        return;
    }

    auto rp = qobject_cast<QNetworkReply*>(sender());
    if (rp == nullptr)
    {
        return;
    }
    if (bytesTotal <= 0)
    {
        return;
    }
    QString postMD5 = rp->property("postMD5").toString();
    download_data_cache_[postMD5].append(rp->readAll());
}

void CHttpSupport::onHttpPostRspFinished()
{
    if (sender() == nullptr)
    {
        return;
    }

    auto rp = qobject_cast<QNetworkReply*>(sender());
    if (rp == nullptr)
    {
        return;
    }

    QByteArray rpData;

    QString url = rp->url().toString();
    QString postMD5 = rp->property("postMD5").toString();
    QByteArray postData = rp->property("data").toByteArray();

    int statusCode = rp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    QString strUrl = rp->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();

    switch (statusCode)
    {
        case 200:
        case 201:
        {
            rpData = download_data_cache_[postMD5];
            QString redirectMD5 = redirect_map_[postMD5];
            QString redirectUrl = redirect_map_[url];
            if (redirectMD5.isEmpty() || redirectUrl.isEmpty())
            {
                std::cout << rpData.toStdString() << std::endl;
                emit httpPostRspReady(url, postMD5, rpData);
            }
            else
            {
                std::cout << rpData.toStdString() << std::endl;
                emit httpPostRspReady(redirectUrl, redirectMD5, rpData);
            }

        }
            break;
        case 301:
        case 302:
        {
            if (!strUrl.isEmpty())
            {
                QString tUrl = redirect_map_[url];//direct by
                if (tUrl.isEmpty())
                    redirect_map_[strUrl] = url;
                else
                    redirect_map_[strUrl] = tUrl;
                QByteArray d;
                d.append(strUrl);
                d.append(postData);
                QString md5 = QCryptographicHash::hash(d, QCryptographicHash::Md5);
                QString tPostMD5 = redirect_map_[md5];//direct by
                if (tPostMD5.isEmpty())
                {
                    redirect_map_[md5] = postMD5;
                }
                else
                {
                    redirect_map_[md5] = tPostMD5;
                }
                httpPost(strUrl, postData);
            }
        }
            break;
        default:
            QString redirectMD5 = redirect_map_[postMD5];
            QString redirectUrl = redirect_map_[url];
            if (redirectMD5.isEmpty() || redirectUrl.isEmpty())
            {
                emit httpPostRspReady(url, postMD5, download_data_cache_[postMD5]);
            }
            else
            {
                emit httpPostRspReady(redirectUrl, redirectMD5, download_data_cache_[postMD5]);
            }
            break;
    }
    clearRp(rp);
}

CHttpSupport &CHttpSupport::instance()
{
    static CHttpSupport instance;
    return instance;
}

void CHttpSupport::httpGet(const QString &url)
{
    if (processing_rq_.value(url, false)) //ignore when rq processing
    {
        return;
    }
    for (auto &vv:redirect_map_)
    {
        if (vv == url) //ignore when redirect processing
        {
            return;
        }
    }
    processing_rq_.insert(url, true);
    QNetworkRequest rq;
    QSslConfiguration config = rq.sslConfiguration();
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    config.setProtocol(QSsl::TlsV1SslV3);
    rq.setSslConfiguration(config);
    rq.setUrl(QUrl(url));
    QNetworkReply* rp = net_access_manager_->get(rq);
    connect(rp, &QNetworkReply::finished, this, &CHttpSupport::onHttpGetRspFinished);
    connect(rp, &QNetworkReply::downloadProgress, this, &CHttpSupport::onHttpGetRspProgress);
}

QByteArray CHttpSupport::httpPost(const QString &url, const QByteArray &data)
{
    QByteArray d;
    d.append(url);
    d.append(data);
    QByteArray md5 = QCryptographicHash::hash(d, QCryptographicHash::Md5);
    if (processing_rq_.value(md5, false)) //ignore when rq processing
    {
        return md5;
    }
    for (auto &vv:redirect_map_)
    {
        if (vv == md5) //ignore when redirect processing
        {
            return md5;
        }
    }
    processing_rq_.insert(md5, true);

    QNetworkRequest rq;
    QSslConfiguration config = rq.sslConfiguration();
    rq.setRawHeader("Content-Type","application/json");
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    config.setProtocol(QSsl::TlsV1SslV3);
    rq.setSslConfiguration(config);

    rq.setUrl(QUrl(url));
    QNetworkReply* rp = net_access_manager_->post(rq, data);
    rp->setProperty("postMD5", md5);
    rp->setProperty("url", url);
    rp->setProperty("data", data);

    connect(rp, &QNetworkReply::finished, this, &CHttpSupport::onHttpPostRspFinished);
    connect(rp, &QNetworkReply::downloadProgress, this, &CHttpSupport::onHttpPostRspProgress);

    return md5;
}
